/*
 * Copyright (C) 2011 Reeny
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.tobidodo.spritmonclient.util;

import static de.tobidodo.spritmonclient.util.LogHelper.logErrorVerbose;
import static de.tobidodo.spritmonclient.util.LogHelper.logInfo;
import static de.tobidodo.spritmonclient.util.LogHelper.logRequestResult;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

import android.util.Log;
import de.tobidodo.spritmonclient.data.UrlConstants;
import de.tobidodo.spritmonclient.data.UserLanguage;
import de.tobidodo.spritmonclient.exception.CarIdNotFoundException;

/**
 * A utility class for creating a request used for sending
 * data to spritmonitor.de.
 * @author user
 *
 */
public class RequestUtil {
	
	/**
	 * Create a list with keys and values which are used to
	 * fill a new form of spritmonitor.de
	 * @param provider A object that provides data for the
	 * 		form.
	 * @return The created list with the keys and values.
	 */
	public static List<NameValuePair> collectTransmissionData(final RequestParamProvider provider) {
		
		List<NameValuePair> fueldata = new ArrayList<NameValuePair>(16);
		fueldata.add(new BasicNameValuePair("base[date]", TextUtil.getCurrentDate("dd.MM.yyyy")));
		fueldata.add(new BasicNameValuePair("base[type]", provider.getFuelType()));
		fueldata.add(new BasicNameValuePair("base[kmpos]", provider.getTotalDistanceValue()));
		fueldata.add(new BasicNameValuePair("base[trip]", provider.getPeriodDistanceFormValue()));
		fueldata.add(new BasicNameValuePair("base[quantity]", provider.getFuelAmountFormValue()));
		fueldata.add(new BasicNameValuePair("base[quantityunit]", "1")); // 1 ist Liter
		fueldata.add(new BasicNameValuePair("base[fuelsort]", "7")); // 7 Super
		fueldata.add(new BasicNameValuePair("base[price]", provider.getFuelPriceFormValue()));
		fueldata.add(new BasicNameValuePair("base[currency]", "0")); // 0 ist EUR
		fueldata.add(new BasicNameValuePair("base[pricetype]", "0")); // 0 ist Gesamtpreis
		fueldata.add(new BasicNameValuePair("base[note]", getCommentText()));
		fueldata.add(new BasicNameValuePair("conditions[tires]", "2")); // 2 ist Winterreifen
		fueldata.add(new BasicNameValuePair("conditions[drivingstyle]", "2")); // 2 ist normal
		fueldata.add(new BasicNameValuePair("conditions[streets][]", "1")); // 1 ist autobahn
		fueldata.add(new BasicNameValuePair("conditions[streets][]", "2")); // 2 ist stadt

		fueldata.add(new BasicNameValuePair("save", "save"));
		return fueldata;
	}
	
	/**
	 * Create a http post request which fills a new form and
	 * send it to spritmonitor.de. The progress is noted to
	 * the given callback.
	 * @param callback A callback object that monitors the
	 * 		request progress.
	 * @param provider A object that provides data for the
	 * 		form.
	 * @param userLanguage 
	 * @param httpClient A http client that is used to send the
	 * 		request.
	 * @return <code>true</code> if the request could be
	 * 		successfull created and send. <code>false</code>
	 * 		for errors.
	 */
	public static boolean submitFuelData(final RequestCallback callback, final RequestParamProvider provider, Long carid, UserLanguage userLanguage, final HttpClient httpClient){


		HttpPost fuelingHttpPost = new HttpPost(getCreateFuelingUri(provider, carid, userLanguage));

		HttpResponse response = null;
		HttpContext httpCtx = new BasicHttpContext();

		callback.onProgressStateChanged(5);

		try {

			List<NameValuePair> fueldata = RequestUtil.collectTransmissionData(provider);

			callback.onProgressStateChanged(8);

			fuelingHttpPost.setEntity(new UrlEncodedFormEntity(fueldata));

			callback.onProgressStateChanged(12);

			response = httpClient.execute(fuelingHttpPost, httpCtx);


			callback.onProgressStateChanged(25);

		} catch (ClientProtocolException e) {
			logErrorVerbose("Error: "+e, e);
		} catch (IOException e) {
			logErrorVerbose("Error: "+e, e);
		}


		int resultcode = response.getStatusLine().getStatusCode();
		Log.i("main","Resultcode: "+resultcode);
		if (resultcode < 200 || resultcode > 204){
			Log.e(RequestUtil.class.getSimpleName(), "Error at sending fuel data. HTTP-State: "+resultcode);
			return false;
		}

		HttpUriRequest currentReq = (HttpUriRequest) httpCtx.getAttribute(ExecutionContext.HTTP_REQUEST);
		HttpHost currentHost = (HttpHost) httpCtx.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
		String currentUrl = currentHost.toURI() + currentReq.getURI();

		callback.onProgressStateChanged(28);

		if (!currentUrl.contains("/detailansicht/") && !currentUrl.contains("/detail/") ){
			logInfo("Error at sending fuel data. The response-URL is not the expected detail overview, but: "+currentUrl);
			return false;
		}

		logRequestResult(response);
		return true;
	}
	
	/**
	 * Try to find the first car id in a HTTP response. 
	 * @param httpResponse The HTTP response where the car id is assumed
	 * 		to be found.
	 * @return The found car id.
	 */
	public static String determineCarIdFromContent(HttpResponse httpResponse){
		
		try{
			InputStream in = httpResponse.getEntity().getContent();
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			
			// the first two dots can be 'de', 'fr' or 'en'
			Pattern idLinkPattern = Pattern.compile("<a href=\"/../(detail|detailansicht)/(\\d+).html\"><b>ID: \\d+</b></a>");
			
			String curHtmlLine = null;
			Matcher lineMatcher;
			while((curHtmlLine = reader.readLine()) != null){
				lineMatcher = idLinkPattern.matcher(curHtmlLine);
				if (lineMatcher.find()){
					String carId = lineMatcher.group(2);
					logInfo("found car id '"+carId+"'");
					return carId;
				}
			}
			in.close();
			
		} catch (IOException ioe){
			throw new CarIdNotFoundException(ioe);
		}
		
		return null;
	}
	

	private static URI getCreateFuelingUri(final RequestParamProvider urlProvider, Long carid, UserLanguage lang){
		UrlConstants urlTokens = UrlConstants.getUrlConstantsForAddFuel(lang);
		
		String subpath = String.format(urlTokens.subpath(), carid);
		
		try {
			URL addFuelUrl = new URL(urlTokens.protocol(), urlTokens.host(), subpath);
			Log.d(RequestUtil.class.getSimpleName(), "add-fuel-url="+String.valueOf(addFuelUrl.toExternalForm()));

			return addFuelUrl.toURI();
		} catch (MalformedURLException e) {
			logErrorVerbose("Error: "+e, e);
		} catch (URISyntaxException e) {
			logErrorVerbose("Error: "+e, e);
		}

		return null;
	}
	
	private static String getCommentText(){
		SimpleDateFormat datetimeFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
		return "entered by Spritmonitor Client at ["+datetimeFormat.format(new Date())+"]";
	}


}
